
#import "QueensController.h"

@implementation QueensController

								// set up board
								// use selected buttons for white squares
- awakeFromNib
	{
	int	i, j;
	
	for (i=0; i LT 8; i++)
		for ( j=0; j LT 8; j++)
			{
			id		cell = [board cellAt: i : j];
			
			[cell setEnabled: NO];
			if ( (i+j)%1 )
				[board selectCell: cell];
			}

	rows = 0;					// only 1 row valid
	column[0] = 0;				// only search last 4 due to symmetry
	[self placeQueenAt: 0 : column[0]];
	[board display];
	return( self);
	}
	
								
	/*	Action messages			*/
	
- motor:sender
	{
	running = YES;
	[self motorStep];
	return( self);
	}
	

- pause: sender
	{
	running = NO;
	return( self);
	}
	

- step: sender
	{
	running = [self countStep];
	return( self);
	}


								// place another queen
- (BOOL)countStep
	{
	BOOL	placed = [self stepFrom: 0];

								// update counter	
	[stepsDisplay setIntValue: [stepsDisplay intValue]+1];
	
								// if placed on row 7, it is solved
								// update solutions and maybe pause
	if ( rows EQ 7 )
		{
		[solutionsDisplay setIntValue: [solutionsDisplay intValue]+1];
		if ( [solutionPause intValue] )
			return( NO);
		}
		
	return( placed);
	}
	

- motorStep
	{
								// iterate single steps,
								// but delay between steps to allow
								// pause message
	if ( running )
		{
		running = [self countStep];

		[self perform: 
				@selector( motorStep) with: nil 
				afterDelay: 20 cancelPrevious: YES
				];
		}

	return( self);		
	}
	

								// set the queen icon for the chosen cell
- placeQueenAt: (int)aRow : (int)aColumn
	{
	id		cell = [board cellAt: aRow : aColumn];
	NXRect	cellFrame;
	
	[cell setIcon: "queen"];
	[board getCellFrame: &cellFrame at: aRow : aColumn];
	[board display: &cellFrame : 1];
	return( self);
	}


								// go back to no icon for the cell
- removeQueenFrom: (int)aRow : (int)aColumn
	{
	id		cell = [board cellAt: aRow : aColumn];
	NXRect	cellFrame;
	
	[cell setIcon: ""];
	[board getCellFrame: &cellFrame at: aRow : aColumn];
	[board display: &cellFrame : 1];
	return( self);
	}


								// recursive scan
- (BOOL)stepFrom: (int)aRow
	{
	int		i;
								//limit recursion to 8 rows	
	if ( aRow LT 8 )
		{
								// if not at end of placed rows
								// go immediately to next row
		if ( aRow LE rows )
			{
			if ( [self stepFrom: aRow+1] )
				return( YES);
				
								// if at empty row, fall through
								// to continue scan
			else
				{
				rows = aRow;
				[self removeQueenFrom: aRow : column[ aRow]];
				column[ aRow] += 1;
				}
			}

								// scan remainder of this row		
		for ( i=column[ aRow]; i LT 8; i++ )
			{
								// try next queen 
								// if no attacks, move to next row
			[self placeQueenAt: aRow : i];
			if ( [self validate: aRow : i] )
				{
				column[ aRow] = i;
				rows = aRow;
				return( YES);
				}

								// remove this queen and continue			
			[self removeQueenFrom: aRow: i];
			}

								// reset this column and back up
		column[ aRow] = 0;
		}

	return( NO);
	}
	

								// check for attacks...
								// another queen in same column
								// or on diagonal
- (BOOL)validate: (int)aRow : (int) aColumn
	{
	int		i;
	
	for ( i=0; i LT aRow; i++ )
		{
		int		rowDistance = aRow - i;
		int		columnDistance = aColumn - column[i];
		
		if ( 
				aColumn EQ column[i]
				|| rowDistance EQ columnDistance
				|| rowDistance EQ -columnDistance
				)
			return( NO);
		}
	
	return( YES);
	}
	

@end
